package com.yuandian.business.monitor.documentExport;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.yuandian.api.business_monitor.po.AdvancedQuery;
import com.yuandian.api.business_monitor.po.QueryBusinessStepParams;
import com.yuandian.api.business_monitor.po.SearchParams;
import com.yuandian.api.business_monitor.vo.BusinessStepResVo;
import com.yuandian.api.management.entity.ConfDocumentExportInfo;
import com.yuandian.api.management.entity.ConfReturnCode;
import com.yuandian.api.management.feign.*;
import com.yuandian.api.management.vo.OptionVo;
import com.yuandian.bpm.common.core.constant.SecurityConstants;
import com.yuandian.bpm.common.core.util.R;
import com.yuandian.business.monitor.mapper.BusinessEndToEndDetailMapper;
import com.yuandian.business.monitor.service.IBusiDetailStatService;
import com.yuandian.business.monitor.service.impl.ExclusiveIndicator;
import com.yuandian.constant.Constants;
import com.yuandian.enums.BaseTableName;
import com.yuandian.utils.*;
import com.yuandian.utils.poi.ExcelTitleUtils;
import com.yuandian.utils.poi.ExcelUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 业务步骤下载
 */
@Slf4j
@Service("businessStepExcelHandler")
public class BusinessStepExcelHandler implements DocumentExportHandler{


	private static final String STEP_TABLE_NAME = "business_step";

	@Autowired
	private IBusiDetailStatService busiDetailStatService;


	@Autowired
	private RemoteExclusiveIndicatorService remoteExclusiveIndicatorService;

	@Autowired
	private RemoteBusinessHallService remoteBusinessHallService;

	@Autowired
	private RemoteEmployeeService remoteEmployeeService;

	@Autowired
	private RemoteMenuMappingService remoteMenuMappingService;

	@Autowired
	private RemoteOptionService remoteOptionService;

	@Autowired
	private RemoteStepService remoteStepService;

	@Autowired
	private RemoteReturnCodeService remoteReturnCodeService;
	@Autowired
	private ExclusiveIndicator exclusiveIndicator;


	@Autowired
	private BusinessEndToEndDetailMapper businessEndToEndDetailMapper;




	/**
	 * 响应状态 和 完整性 映射关系
	 */
	private static Map<String, String> dealStateMap = new HashMap<>();
	private static Map<String, String> isCompletionMap = new HashMap<>();

	@PostConstruct
	public void init() {
		dealStateMap.put("0", "失败");
		dealStateMap.put("1", "成功");

		isCompletionMap.put("0", "完整");
		isCompletionMap.put("1", "无响应");
		isCompletionMap.put("2", "请求不完整");
		isCompletionMap.put("3", "响应不完整");

	}

	/**
	 * 下载文件的处理逻辑
	 *
	 * @param confDocumentExportInfo
	 */
	@Override
	public void handle(ConfDocumentExportInfo confDocumentExportInfo) throws JsonProcessingException {
		//获取系统现在的时间作为开始时间(时间戳)
		long startTime = System.currentTimeMillis();

		QueryBusinessStepParams params = null;
		params = JSONUtil.toBean(confDocumentExportInfo.getDocExportParameterJson(), QueryBusinessStepParams.class);
		// 获取需要查询的表名, 字段名,前缀为business_step
		List<String> tablesList = DateTool.getTablesName(STEP_TABLE_NAME, params.getStartTime(), params.getEndTime());
		//将表名集合转为字符串数组
		String[] tableNames = tablesList.toArray(new String[tablesList.size()]);
		//获取ES查询字段
		List<String> fields = getFields();


		// 初始化数据集（步骤汇总表, 步骤失败明细表, 步骤告警表）
		List<List<Object>> allStepList = new ArrayList<>();
		List<List<Object>> failStepList = new ArrayList<>();
		List<List<Object>> alarmStepList = new ArrayList<>();
		Map<String, LinkedHashMap<String, Object>> alarmMap = new HashMap<>();
		// 查询配置系统专属指标自定义选择字段

		// 初始化 sheet 下标
		Integer sheetNo = 1;

		params.setDealTimeFieldIndex(-1);
		// 计算 dealTime 所处的列下标,displayMappingFields为列表需要展示的字段,dealTime属于可选字段之一.
		getDealTimeFieldIndexFromDisplayFields(params.getDisplayMappingFields(), params);

		ExcelWriter excelWriter = null;
		OutputStream outputStream = null;
		try {
			outputStream = new FileOutputStream(confDocumentExportInfo.getExportFileName());
			// 实例化 ExcelWriter 对象
			excelWriter = EasyExcel.write(outputStream)
					.registerWriteHandler(new SimpleColumnWidthStyleStrategy(20))
					.registerWriteHandler(new SimpleRowHeightStyleStrategy((short) 30, (short) 20))
					.build();

			// 1.进行步骤汇总表Sheet导出
			sheetNo = performAllStepDataDownload(allStepList, failStepList, alarmMap, sheetNo, excelWriter, params, fields, tableNames);
			// 2.进行步骤失败明细表Sheet导出
			sheetNo = performFailStepDataDownload(failStepList, sheetNo, excelWriter, params);
			// 3.进行步骤告警表Sheet导出
			performAlarmStepDataDownload(alarmStepList, alarmMap, sheetNo, excelWriter, params);


			// 返回文件流
//            ExcelUtil.writeExcel("业务步骤信息表", response);

		} catch (Exception e) {
			log.error("业务步骤列表下载异常!" + e.getMessage());
			throw new RuntimeException("业务步骤列表下载异常");
		} finally {
			if (excelWriter != null) {
				try {
					outputStream.flush();
					excelWriter.finish();
					outputStream.close();
				} catch (Exception e) {
					log.error("IO流关闭异常： " + e.getMessage());
					throw new RuntimeException("IO流关闭异常");
				}
			}
		}

		log.info("业务步骤导出成功,耗时: {}ms", (System.currentTimeMillis() - startTime));
	}






	/**
	 * 进行步骤告警表导出
	 *
	 * @param alarmStepList 失败步骤数据集
	 * @param sheetNo       sheet下标
	 * @param excelWriter   excel对象
	 * @param params        查询参数
	 * @return
	 */
	private Integer performAlarmStepDataDownload(List<List<Object>> alarmStepList,
												 Map<String, LinkedHashMap<String, Object>> alarmMap,
												 Integer sheetNo,
												 ExcelWriter excelWriter,
												 QueryBusinessStepParams params) {
		try {
			// 初始化Excel标题, 序号
			Integer indexNo = 1;
			String alarmTitle = "步骤告警表";
			String alarmFormTitleName = getFormTitleName(alarmTitle, params);
			List<List<String>> alarmHeadList = new ArrayList<>();
			List<String> displayAlarmMappingFields = new ArrayList<>();
			List<String> displayFields = getDisplayFields2(params.getDisplayFields(), params.getDisplayMappingFields(), displayAlarmMappingFields);
			ExcelUtil.executeUpdateHeader(displayFields, alarmHeadList, alarmFormTitleName);

			// 导出数据
			if (alarmMap != null) {
				// 没数据时则写入空表
				if (alarmMap.size() == 0) {
					ExcelUtil.writeOnly(excelWriter, alarmStepList, alarmHeadList, sheetNo++, alarmTitle, null);
					return sheetNo;
				}
				// 查询数据
				List<Map<String, Object>> resList = new ArrayList<>();

				params.setPageNum(1);
				params.setPageSize(6000);
				params.setIsAlarm(1);
				// 进行数据查询
				R result = busiDetailStatService.getBusinessListForStepExport(params);
				PageDataInfo pageDataInfo = BeanUtils.cast(result.getData());
				List<Map<String, Object>> alarmList = BeanUtils.cast(pageDataInfo.getData());
				// 加入导出集合
				resList.addAll(alarmList);

				alarmList.clear();
				long totality = pageDataInfo.getTotal();
				// 总页数
				long totalPage = (totality / 6000) + (totality % 6000 == 0 ? 0 : 1);
				// 分页请求数据(i = 2 是因为上面已经请求了第一页的数据)
				for (long i = 2; i <= totalPage; i++) {
					params.setPageNum(i);
					result = busiDetailStatService.getBusinessListForStepExport(params);
					pageDataInfo = BeanUtils.cast(result.getData());
					alarmList = BeanUtils.cast(pageDataInfo.getData());
//                    alarmList = BeanUtils.convert(bdVoList, BusinessStepAlarmVo.class);
					// 若达到sheet最大值则进行写入： 11 = sheet允许的最大行数 / 每次查询的条数pageSize
					if (resList != null && resList.size() >= Constants.SHEET_MAX_ROW) {
						// 转换结果集，并回填 序号, 菜单ID, 菜单名称, 页签, 页面大小, 按钮, 按钮名称 等字段
						alarmStepList = getExcelList(resList, displayAlarmMappingFields, alarmMap);
						// 更新序号
						for (List<Object> list : alarmStepList) {
							list.set(0, indexNo++);
						}

						ExcelUtil.writeOnly(excelWriter, alarmStepList, alarmHeadList, sheetNo++, alarmTitle, null);
						resList.clear();
						alarmStepList.clear();
					}
					resList.addAll(alarmList);
					alarmList.clear();
				}

				// 对剩余的数据进行写入
				if (resList.size() > 0) {

					// 转换结果集，并回填 序号, 菜单ID, 菜单名称, 页签, 页面大小, 按钮, 按钮名称 等字段
					alarmStepList = getExcelList(resList, displayAlarmMappingFields, alarmMap);
					// 更新序号
					for (List<Object> list : alarmStepList) {
						list.set(0, indexNo++);
					}

					ExcelUtil.writeOnly(excelWriter, alarmStepList, alarmHeadList, sheetNo++, alarmTitle, null);
					resList.clear();
				}
			}
		} catch (Exception e) {
			log.error("步骤告警Sheet导出失败: {}", e.getMessage(), e);
		}

		return sheetNo;
	}

	/**
	 * 针对告警数据Sheet进行参数转换
	 *
	 * @param resList
	 * @param displayAlarmMappingFields
	 * @param alarmMap
	 * @return
	 */
	private List<List<Object>> getExcelList(List<Map<String, Object>> resList,
											List<String> displayAlarmMappingFields,
											Map<String, LinkedHashMap<String, Object>> alarmMap) {

		List<List<Object>> excelList = new ArrayList<>();
		if (ListUtils.isNotNull(displayAlarmMappingFields) && ListUtils.isNotNull(resList)) {
			for (Map<String, Object> objectMap : resList) {
				// 转化成行数据
				List<Object> rowList = new ArrayList<>();
				// 初始化Excel序号
				rowList.add(-1);
				LinkedHashMap<String, Object> alarmChildMap = alarmMap.get(objectMap.get("relvanceStepId") + "");
				for (String field : displayAlarmMappingFields) {
					if (!"indexId".equals(field) && StringUtils.isNotEmpty(objectMap.get(field) + "")) {
						Object o = objectMap.get(field);
						if ("alarmTriggerTime".equals(field)) {
							LocalDateTime alarmTriggerTime = (LocalDateTime) o;
							DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
							String format = alarmTriggerTime.format(dateTimeFormatter);
							rowList.add(format);
						} else {
							rowList.add(o);
						}
					} else if (!"indexId".equals(field) && alarmChildMap != null) {
						log.info("[步骤告警下载][alarmChildMap != null]field: {}, relvanceStepId: {}", field, objectMap.get("relvanceStepId") + "");
						rowList.add(alarmChildMap.get(field));
					}
				}
				excelList.add(rowList);
			}
		}
		return excelList;
	}


	/**
	 * 进行步骤失败明细表导出
	 *
	 * @param failStepList 失败步骤数据集
	 * @param sheetNo      sheet下标
	 * @param excelWriter  excel对象
	 * @param params       查询参数
	 * @return
	 */
	private Integer performFailStepDataDownload(List<List<Object>> failStepList,
												Integer sheetNo,
												ExcelWriter excelWriter,
												QueryBusinessStepParams params) {
		HashMap<Integer, List<Integer>> excelStyleMap = new HashMap<>();
		try {
			// 初始化Excel标题, 序号
			Integer indexNo = 1;
			String failStepTitle = "步骤失败明细表";
			String failStepFormTitleName = getFormTitleName(failStepTitle, params);
			List<List<String>> failStepHeadList = new ArrayList<>();
			List<String> displayFields = new ArrayList<>();
			displayFields.add("序号");
			displayFields.addAll(params.getDisplayFields());
			ExcelUtil.executeUpdateHeader(displayFields, failStepHeadList, failStepFormTitleName);

			// 自定义导出字段
//            HashMap<String, String> businessAlarmVoFields = getBusinessStepVoFields();
//            List<String> excludeColumnFields = getExcludeColumnFields(businessAlarmVoFields, params.getDisplayMappingFields());

			// 导出数据
			if (failStepList != null) {
				// 没数据则写空表
				if (failStepList.size() == 0) {
					ExcelUtil.writeOnly(excelWriter, failStepList, failStepHeadList, sheetNo++, failStepTitle, null);
					return sheetNo;
				}

				// 更新下标
                /*for (List<Object> rowList : failStepList) {
                    rowList.set(0, indexNo++);
                }*/

				// 手动分页导出数据
				List<List<List<Object>>> lists = ListUtils.partList(failStepList, Constants.SHEET_MAX_ROW);
				for (List<List<Object>> stepResExcelVoList : lists) {
					for (int i = 0; i < stepResExcelVoList.size(); i++) {
						// 获取行数据
						List<Object> rowList = stepResExcelVoList.get(i);
						// 获取 dealTime 大于 5秒 的数据在哪一行
						if (params.getDealTimeFieldIndex() != -1 && params.getDealTimeFieldIndex() < stepResExcelVoList.get(0).size()) {
							if ((Long.valueOf(String.valueOf(rowList.get(params.getDealTimeFieldIndex()))) / 1000) >= 5000) {
								// +2 是因为Excel开头的两行是标题和表头
								excelStyleMap.put(i + 2, Arrays.asList(params.getDealTimeFieldIndex()));
							}
							// 给字段 dealTime 进行单位转换
							rowList.set(params.getDealTimeFieldIndex(), rowList.get(params.getDealTimeFieldIndex()) == null ? "" : DateTool.getMinSecond(getDouble(Double.parseDouble(String.valueOf(rowList.get(params.getDealTimeFieldIndex()))) / 1000)));
						}
						// 更新Excel序号
						rowList.set(0, indexNo++);
					}

					ExcelUtil.writeOnly(excelWriter, stepResExcelVoList, failStepHeadList, sheetNo++, failStepTitle, excelStyleMap);
//                    stepResExcelVoList.clear();
					excelStyleMap.clear();
				}
				if (lists != null) {
					lists.clear();
				}
			}

		} catch (Exception e) {
			log.error("失败步骤Sheet导出失败: {}", e.getMessage(), e);
		}

		return sheetNo;
	}

	/**
	 * 进行步骤汇总表导出
	 *
	 * @param allStepList     步骤汇总数据集
	 * @param failStepList    失败步骤数据集（allStepList中 dealState = 0 的数据）
	 * @param alarmMap        告警步骤数据集（allStepList中 dealState = 1 的数据的部分字段数据）
	 * @param sheetNo         sheet下标
	 * @param excelWriter     excel对象
	 * @param params          查询参数
	 * @param fields
	 * @param queryTableNames
	 * @return
	 */
	private int performAllStepDataDownload(List<List<Object>> allStepList,
										   List<List<Object>> failStepList,
										   Map<String, LinkedHashMap<String, Object>> alarmMap,
										   int sheetNo,
										   ExcelWriter excelWriter,
										   QueryBusinessStepParams params,
										   List<String> fields,
										   String[] queryTableNames) {
		HashMap<Integer, List<Integer>> excelStyleMap = new HashMap<>();
		try {
			// 初始化Excel标题, 序号
			Integer indexNo = 1;
			//设置sheet名
			String stepTitle = "步骤汇总表";
			//设置标题名
			String stepFormTitleName = getFormTitleName(stepTitle, params);
			//创建headList
			List<List<String>> stepHeadList = new ArrayList<>();
			List<String> displayFields = new ArrayList<>();
			//在需展示字段中添加"序号"列,放在首列
			displayFields.add("序号");
			//添加后续需要展示的列
			List<String> displayFields1 = params.getDisplayFields();
			displayFields.addAll(displayFields1);
			//更新表头,需要展示字段集合,标题名,聚合成headList
			ExcelUtil.executeUpdateHeader(displayFields, stepHeadList, stepFormTitleName);

			// 查询配置系统专属指标自定义选择字段
			LinkedHashMap<String, Object> customMap = exclusiveIndicator.getCustomMapInner();

			// 初始化查询对象
			int sheetPage = 1;
			long total = 0;
			// 初始化序号
			int index = 0;
			//写入行数
			int writeNum = 0;
			//分页参数
			int pageNum = 1, pageSize = 10000;
			// 导出数据
			// 设置查询条件进行分页查询并返回结果
			IPage page = new Page(pageNum, pageSize);
			params.setDisplayFields(fields);
			paramsHandle(params);
			while (true) {
				//step1: 数据查询
				// 判断查询 定性详单表(dwd_business_step) 还是 未定性的详单表(dwd_business_step_unsure)
				if (params.getOrderByColumn() == null||("").equals(params.getOrderByColumn())) {
					// 设置排序，默认按时间降序
					params.setOrderByColumn("end_time");
					params.setIsAsc("desc");
				}
				params.setTableName(params.getIsUnsure() == 0 ? BaseTableName.BUSINESS_STEP.getName() : BaseTableName.BUSINESS_STEP_UNSURE.getName());
				List<BusinessStepResVo> businessStepResVos = businessEndToEndDetailMapper.queryBusinessStepListPage(page, params);
				//step2: 数据整理
				// 执行数据封装
				executeEncapsulateData2(allStepList, businessStepResVos, alarmMap, failStepList, indexNo, params.getDisplayMappingFields(), customMap);
				page.setCurrent(++pageNum);
				if (allStepList != null && allStepList.size() >= 0) {
					//对数据进行处理：1、时间转换 2、超时标红行号记录
					for (int i = 0; i < allStepList.size(); i++) {
						// 获取行数据
						List<Object> rowData = allStepList.get(i);
						// 获取 dealTime 大于 5秒 的数据在哪一行
						if (params.getDealTimeFieldIndex() != -1 && params.getDealTimeFieldIndex() < allStepList.get(0).size()) {
							if ((Long.valueOf(String.valueOf(rowData.get(params.getDealTimeFieldIndex()))) / 1000) >= 5000) {
								// +2 是因为Excel开头的两行是标题和表头,处理时间超过5秒的标红
								excelStyleMap.put(i + 2, Arrays.asList(params.getDealTimeFieldIndex()));
							}
							// 给字段 dealTime 进行单位转换
							rowData.set(params.getDealTimeFieldIndex(), rowData.get(params.getDealTimeFieldIndex()) == null ? "" : DateTool.getMinSecond(getDouble(Double.parseDouble(String.valueOf(rowData.get(params.getDealTimeFieldIndex()))) / 1000)));
						}
						// 更新Excel序号
						rowData.set(0, indexNo++);
						index++;
					}
					//step3: 数据写出
					//如果行数超过60000另起一页
					if (index >= Constants.SHEET_MAX_ROW * sheetNo) {
						ExcelUtil.writeOnly(excelWriter, allStepList, stepHeadList, ++sheetNo, stepTitle, excelStyleMap);
					}else {
						//正常数据写出
						ExcelUtil.writeOnly(excelWriter, allStepList, stepHeadList, sheetNo, stepTitle, excelStyleMap);
					}
					writeNum = index;
					if (pageNum > page.getPages() || page.getTotal() == 0) {
						log.info("共导出数据：{}条，当前页数：{}", index, pageNum);
						break;
					}
					allStepList.clear();
					excelStyleMap.clear();
				}
			}
			params.setDisplayFields(displayFields1);
		} catch (Exception e) {
			log.error("步骤明细Sheet导出失败: {}", e.getMessage(), e);
		}

		return ++sheetNo;
	}

	/**
	 * 计算 dealTime 所处的列下标
	 *
	 * @param displayMappingFields
	 * @param params
	 */
	private void getDealTimeFieldIndexFromDisplayFields(List<String> displayMappingFields, QueryBusinessStepParams params) {
		if (ListUtils.isNotNull(displayMappingFields) && displayMappingFields.contains("dealTime")) {
			// +1 是因为displayFields 前面还有个 序号 字段
			params.setDealTimeFieldIndex(displayMappingFields.indexOf("dealTime") + 1);
		}
	}

	/**
	 * 获取需要展示的字段
	 *
	 * @param displayFields
	 * @return
	 */
	private List<String> getDisplayFields2(List<String> displayFields, List<String> displayMappingFields, List<String> displayAlarmMappingFields) {
		// 定义两组固定下载的列,因为这两组字段之间还要动态判断是否加入其它字段，如：菜单名称，菜单ID。。等
		List<String> fields1 = Arrays.asList("序号", "告警对象", "告警级别", "告警现象", "告警类型", "返回码");
		List<String> fields2 = Arrays.asList("告警时间");

		List<String> fieldsMapping1 = Arrays.asList("indexId", "alarmObject", "alarmLevelName", "alarmContext", "alarmIndicatorName", "alarmResultCode");
		List<String> fieldsMapping2 = Arrays.asList("alarmTriggerTime");

		// 定义动态变化的字段
		List<String> splicingFieldMappingList = Arrays.asList("menuId", "menuName", "pageLabelId", "pageSize", "buttonId", "buttonName");

		// 记录自定义列要下载字段的下标
		List<Integer> fieldIndex = new ArrayList<>();
		displayAlarmMappingFields.addAll(fieldsMapping1);
		for (int i = 0; i < displayMappingFields.size(); i++) {
			String s = displayMappingFields.get(i);
			if (splicingFieldMappingList.contains(s)) {
				displayAlarmMappingFields.add(s);
				fieldIndex.add(i);
			}
		}
		displayAlarmMappingFields.addAll(fieldsMapping2);
		List<String> wantedShowField = new ArrayList<>();

		wantedShowField.addAll(fields1);
		if (ListUtils.isNotNull(fieldIndex)) {
			for (Integer index : fieldIndex) {
				wantedShowField.add(displayFields.get(index));
			}
		}
		wantedShowField.addAll(fields2);

		// 最后拼接前端在专属指标下面所有选的专属指标自定义字段
		Map<String, Object> customMap = new HashMap<>();
		try {
			customMap = remoteExclusiveIndicatorService.assemblyDisplayField(null);
		}catch (Exception e){
			log.error("[异步下载][业务步骤下载]：调用配置平台获取自定义专属指标异常，原因：{}", e.getMessage());
		}
		for (int i = 0; i < displayMappingFields.size(); i++) {
			String field = displayMappingFields.get(i);
			if ("exclusiveIndicator".equals(field) || StringUtils.isNotEmpty(String.valueOf(customMap.get(field)))) {
				displayAlarmMappingFields.add(field);
				wantedShowField.add(displayFields.get(i));
			}
		}

		return wantedShowField;
	}

	/**
	 * 告警实体类所有字段
	 *
	 * @return
	 */
	private List<String> getAllAlarmField() {
		List<String> allAlarmFields = new ArrayList<>();

		allAlarmFields.add("序号");
		allAlarmFields.add("告警对象");
		allAlarmFields.add("告警级别");
		allAlarmFields.add("告警现象");
		allAlarmFields.add("告警类型");
		allAlarmFields.add("返回码");

		allAlarmFields.add("菜单编码");
		allAlarmFields.add("菜单名称");
		allAlarmFields.add("页签");
		allAlarmFields.add("页面大小(条)");
		allAlarmFields.add("按钮");
		allAlarmFields.add("按钮名称");

		allAlarmFields.add("告警时间");
		allAlarmFields.add("专属指标");
		return allAlarmFields;
	}


	/**
	 * 获取Excel大标题名称
	 *
	 * @param title
	 * @param params
	 * @return
	 */
	private String getFormTitleName(String title, QueryBusinessStepParams params) {
		List<String> titleParams = new ArrayList<>();
		titleParams.add(title);
		titleParams.add(params.getSystemName());
		String formTitleName = ExcelTitleUtils.formTitleName(titleParams, params.getStartTime(), params.getEndTime());
		return formTitleName;
	}


	/**
	 * 进行数据封装
	 *
	 * @param resList
	 * @param businessStepResVos
	 * @param alarmMap             记录告警步骤 Map<步骤ID, Map<步骤各个字段, 步骤各个字段值>
	 * @param failStepList
	 * @param stepIndex
	 * @param displayMappingFields
	 * @return
	 */
	private List<List<Object>> executeEncapsulateData2(List<List<Object>> resList,
													   List<BusinessStepResVo> businessStepResVos,
													   Map<String, LinkedHashMap<String, Object>> alarmMap,
													   List<List<Object>> failStepList,
													   int stepIndex,
													   List<String> displayMappingFields,
													   LinkedHashMap<String, Object> customMap) {


		// 获取查询营业厅信息 Map<营业厅编码， 营业厅名称>
		Map<String, String> businessHallMap = new HashMap<>();
		// 获取操作员映射信息 Map<操作员ID, 操作员名称>
		Map<String, String> operatorMap = new HashMap<>();
		// 获取菜单映射信息 Map<菜单ID, 菜单名称>
		Map<String, String> menuMap = new HashMap<>();
		// 获取按钮映射信息 Map<按钮ID, 按钮名称>
		Map<String, String> buttonMap = new HashMap<>();
		// 获取步骤类型映射信息 Map<业务编码@步骤编码, 步骤类型>
		Map<String, String> stepTypeMap = new HashMap<>();
		// 获取 自定义返回码注释 回填到前端
		Map<Integer, String> codeMap = new HashMap<>();

		try {
			// 获取查询营业厅信息 Map<营业厅编码， 营业厅名称>
			businessHallMap = remoteBusinessHallService.getBusinessHallMapInner(new SearchParams(), SecurityConstants.FROM_IN).getData();
			// 获取操作员映射信息 Map<操作员ID, 操作员名称>
			operatorMap = remoteEmployeeService.getOperatorMap(new SearchParams(), SecurityConstants.FROM_IN);
			// 获取菜单映射信息 Map<菜单ID, 菜单名称>
			menuMap = remoteMenuMappingService.getMenuMap(new SearchParams(), SecurityConstants.FROM_IN);
			// 获取按钮映射信息 Map<按钮ID, 按钮名称>
			buttonMap = remoteOptionService.getButtonMap(SecurityConstants.FROM_IN).getData().stream().collect(Collectors.toMap(OptionVo::getValue, OptionVo::getLabel));
			// 获取步骤类型映射信息 Map<业务编码@步骤编码, 步骤类型>
			stepTypeMap = remoteStepService.queryStepTypeByBusinessCode(SecurityConstants.FROM_IN).getData();
			// 获取 自定义返回码注释 回填到前端 Map<系统名称@返回码, 返回码注释>
			codeMap = getCodeExplain();
		}catch (Exception e){
			log.error("[业务监控][业务步骤下载]调用远程接口获取配置异常，原因：{}" , e.getMessage());
		}
		// 获取办事处称呼 单位/营业厅
		String explain = "";
		// 处理结果
		for (BusinessStepResVo businessStepResVo : businessStepResVos) {
			// 记录告警步骤行数据
			LinkedHashMap<String, Object> alarmChildMap = new LinkedHashMap<>();

			// 保存行数据的横向List
			List<Object> rowList = new ArrayList<>();
			// 初始化序号(默认 -1， 上层方法进行更新)
			rowList.add(-1);
			Map<String, Object> sourceMap = BeanUtil.beanToMap(businessStepResVo);
			// 从后台配置获取需要从专属指标提取的字段并从专属指标中提取该字段对应的值
			LinkedHashMap<String, Object> customIndicatorMap = ExclusiveIndicator.getValueFromSpecifyParameters(customMap, sourceMap.get("exclusiveIndicator") + "");
			if (customIndicatorMap == null) {
				customIndicatorMap = new LinkedHashMap<>();
			}

			if (ListUtils.isNotNull(displayMappingFields)) {
				for (String field : displayMappingFields) {
					Object value = sourceMap.get(field);
					// 对字段进行特殊处理
					if ("customResCode".equals(field)) {
						explain = codeMap.get(sourceMap.get("systemName") + "@" + sourceMap.get("customResCode"));
						explain = StringUtils.isEmpty(explain) ? "" : (" (" + explain + ")");
						value = sourceMap.get("customResCode") + explain;

					} else if ("operatorName".equals(field)) {
						value = StringUtils.isNotEmpty(sourceMap.get("operatorId") + "") ?
								(StringUtils.isNotEmpty(operatorMap.get(sourceMap.get("operatorId") + "")) ? operatorMap.get(sourceMap.get("operatorId") + "") : "-") : "-";

					} else if ("menuName".equals(field)) {
						value = StringUtils.isNotEmpty(sourceMap.get("menuId") + "") ?
								(StringUtils.isNotEmpty(menuMap.get(sourceMap.get("menuId") + "")) ? menuMap.get(sourceMap.get("menuId") + "") : "-") : "-";
						sourceMap.put("menuName", value);

					} else if ("buttonName".equals(field)) {
						value = StringUtils.isNotEmpty(sourceMap.get("buttonId") + "") ?
								(StringUtils.isNotEmpty(buttonMap.get(sourceMap.get("buttonId") + "")) ? buttonMap.get(sourceMap.get("buttonId") + "") : "-") : "-";
						sourceMap.put("buttonName", value);

					} else if ("businessHallName".equals(field)) {
						value = StringUtils.isNotEmpty(sourceMap.get("businessHall") + "") ?
								(StringUtils.isNotEmpty(businessHallMap.get(sourceMap.get("businessHall") + "")) ? businessHallMap.get(sourceMap.get("businessHall") + "") : "-") : "-";

					} else if ("dealState".equals(field)) {
						value = dealStateMap.get(sourceMap.get("dealState") + "");

					} else if ("isCompletion".equals(field)) {
						value = isCompletionMap.get(sourceMap.get("isCompletion") + "");

					} else if ("stepType".equals(field)) {
						value = stepTypeMap.get(sourceMap.get("businessCode") + "@" + sourceMap.get("stepCode"));

					} else if (!"exclusiveIndicator".equals(field) && customIndicatorMap.get(field) != null) {
						value = customIndicatorMap.get(field);
					}
					rowList.add(value);
				}
			}
			resList.add(new ArrayList<>(rowList));
			// 保存失败步骤
			if (0 == Integer.parseInt(sourceMap.get("dealState") + "")) {
				failStepList.add(new ArrayList<>(rowList));
			}

			// 保存告警步骤
			if (1 == Integer.parseInt(sourceMap.get("isAlarm") + "")) {
				alarmChildMap.put("relvanceStepId", sourceMap.get("id") + "");
				alarmChildMap.put("menuId", sourceMap.get("menuId") + "");
				alarmChildMap.put("menuName", sourceMap.get("menuName") + "");
				alarmChildMap.put("buttonId", sourceMap.get("buttonId") + "");
				alarmChildMap.put("buttonName", sourceMap.get("buttonName") + "");
				alarmChildMap.put("pageLabelId", sourceMap.get("pageLabelId") + "");

				alarmMap.put(sourceMap.get("id") + "", alarmChildMap);
//                alarmStepList.add(alarmVo);
			}
		}

		return resList;
	}

	public static Double getDouble(Double allTime) {
		if (allTime == null) {
			return 0d;
		}
		return new BigDecimal(allTime).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 获取自定义返回码注释
	 *
	 * @return
	 */
	public Map<Integer, String> getCodeExplain() {

		Map<Integer, String> resMap = new HashMap<>();
		ConfReturnCode params = new ConfReturnCode();
		// 4:自定返回码类型  3: 数据库返回码 2：中间件返回码  1：web 返回码
		params.setTypeId(4);
		List<ConfReturnCode> list = remoteReturnCodeService.getCodeDetail(params, SecurityConstants.FROM_IN).getData();
		if (list != null && list.size() > 0) {
			resMap = list.stream().collect(Collectors.toMap(ConfReturnCode::getId, ConfReturnCode::getExplain));
		}
		return resMap;
	}



	private QueryBusinessStepParams paramsHandle(QueryBusinessStepParams params) {


		// 营业厅名称或ID
		if (StringUtils.isNotEmpty(params.getBusinessHallIdOrName())) {
			if (params.getBusinessHallType() == 2) {
				// 根据前端传过来的单位名称列表获取对应的单位ID列表（因为 财厅项目 一个单位名称可能对应多个单位ID）
				List<String> businessHallIdList = getBusinessHallIdList(params);
				if (ListUtils.isNotNull(businessHallIdList)) {
					params.setBusinessHallIdList(businessHallIdList);
				}
			}
		}

		// 操作员名称或ID
		if (StringUtils.isNotEmpty(params.getOperatorIdOrName())) {
			if (params.getOperatorType() == 2) {
				// 根据前端传过来的操作员名称获取对应的操作员ID列表（因为 财厅项目 一个操作员名称可能对应多个操作员）
				List<String> operatorIdList = getOperatorIdList(params);
				params.setOperatorNameList(operatorIdList);
			}
		}
		//4.1 判断高级查询
		List<AdvancedQuery> advancedQuery = params.getAdvancedQuery();
		//将列名转换成下划线命名
		if (advancedQuery != null) {
			advancedQuery.forEach(e -> e.setColumn(StringUtils.toUnderlineName(e.getColumn())));
			params.setAdvancedQuery(advancedQuery);
		}
		return params;
	}

	private List<String> getOperatorIdList(QueryBusinessStepParams params) {

		List<String> operatorIdList = new ArrayList<>();
		if (StringUtils.isNotEmpty(params.getOperatorIdOrName())) {
			operatorIdList = remoteEmployeeService.getOperatorIdListByOperatorNameList(params.getOperatorIdOrName(), SecurityConstants.FROM_IN).getData();
		}

		return operatorIdList;
	}

	/**
	 * 根据营业厅名称获取营业厅Id列表
	 *
	 * @param params
	 * @return
	 */
	private List<String> getBusinessHallIdList(QueryBusinessStepParams params) {

		List<String> businessHallIdList = new ArrayList<>();
		if (StringUtils.isNotEmpty(params.getBusinessHallIdOrName())) {
			businessHallIdList = remoteBusinessHallService.getBusinessHallIdListByHallNameList(params.getBusinessHallIdOrName(), SecurityConstants.FROM_IN).getData();
		}

		return businessHallIdList;
	}

	/**
	 * 获取ES查询字段
	 *
	 * @return
	 */
	private List<String> getFields() {
		String[] fields = {
				"id", "business_detail_id", "resource_code", "system_id", "system_name",
				"business_code", "business_name", "step_code", "step_name", /*"stepType",*/
				"business_hall", "operator_id",/* "menuId",*/ /*"pageLabelId",*/ /*"pageSize",*/
				/*"buttonId",*/ "deal_state", "is_completion", "custom_res_code", "user_identify",
				"alarm_count", "start_time", "end_time", /*"startTimeUsec", "endTimeUsec",*/
				"deal_time", "request_bytes", "response_bytes", "exclusive_indicator", "is_alarm"
		};
		List<String> fieldList = Arrays.stream(fields).collect(Collectors.toList());
		// 把配置库所配置的自定义字段拼接在查询字段集合里
		LinkedHashMap<String, Object> customMap = exclusiveIndicator.getCustomMapInner();
		if (customMap != null && customMap.size() > 0) {
			for (Map.Entry<String, Object> entry : customMap.entrySet()) {
				String key = entry.getKey();
				fieldList.add(StringUtils.toUnderlineName(key));
			}
		}
//        String[] resField = fieldList.toArray(new String[fieldList.size()]);

		return fieldList;
	}


}
